/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
 * Allwinner SoCs display driver.
 *
 * Copyright (C) 2016 Allwinner.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#if IS_ENABLED(CONFIG_AW_DISP2_SUPPORT_ENAHNCE)
#include "disp_enhance.h"

struct disp_enhance_private_data {
	u32 reg_base;
	u32 enabled;
	bool applied;

	struct disp_enhance_config config;

	 s32 (*shadow_protect)(u32 sel, bool protect);
};
static spinlock_t enhance_data_lock;

static struct disp_enhance *enhances;
static struct disp_enhance_private_data *enhance_private;

struct disp_enhance *disp_get_enhance(u32 disp)
{
	u32 num_screens;

	num_screens = bsp_disp_feat_get_num_screens();
	if (disp >= num_screens) {
		DE_WARN("disp %d out of range\n", disp);
		return NULL;
	}

	return &enhances[disp];
}

static struct disp_enhance_private_data *disp_enhance_get_priv(struct
							       disp_enhance
							       *enhance)
{
	if (enhance == NULL) {
		DE_INFO("NULL hdl\n");
		return NULL;
	}

	return &enhance_private[enhance->disp];
}

static s32 disp_enhance_sync(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	if (disp_feat_is_using_rcq(enhance->disp))
		return 0;


	enhance->update_regs(enhance);
	if (enhancep->enabled)
		disp_al_enhance_sync(enhance->disp);

	return 0;
}

static s32 disp_enhance_tasklet(struct disp_enhance *enhance)
{
	/* unsigned long flags; */
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	if (enhancep->enabled)
		disp_al_enhance_tasklet(enhance->disp);

	return 0;
}

static s32 disp_enhance_update_regs(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	/* size likely change while it depend on layer size */
	/* if (enhancep->applied) { */
	{
		disp_al_enhance_update_regs(enhance->disp);
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->applied = false;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	return 0;
}

static s32 disp_enhance_apply(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	struct disp_enhance_config config;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}
	DE_INFO("disp_enhance_apply, screen %d\n", enhance->disp);
	memset(&config, 0, sizeof(struct disp_enhance_config));
	spin_lock_irqsave(&enhance_data_lock, flags);
	if (enhancep->config.flags != 0) {
		memcpy(&config, &enhancep->config,
		       sizeof(struct disp_enhance_config));
		enhancep->applied = true;
	}
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	if (config.flags != ENH_NONE_DIRTY) {
		disp_enhance_shadow_protect(enhance, 1);
		disp_al_enhance_apply(enhance->disp, &config);
		disp_enhance_shadow_protect(enhance, 0);
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	if (config.flags != 0)
		enhancep->applied = true;
	config.flags = ENH_NONE_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	return 0;
}

static s32 disp_enhance_force_apply(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	struct disp_device *dispdev = NULL;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}
	if (enhance->manager)
		dispdev = enhance->manager->device;
	if (dispdev)
		dispdev->get_resolution(dispdev,
					&enhancep->config.info.size.width,
					&enhancep->config.info.size.height);

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.flags |= ENH_ALL_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);
	disp_enhance_apply(enhance);
	disp_enhance_update_regs(enhance);
	disp_enhance_sync(enhance);

	return 0;
}

/* seem no meaning */
static bool disp_enhance_is_enabled(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return false;
	}

	return enhancep->enabled;
}

static s32 disp_enhance_enable(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	struct disp_device *dispdev = NULL;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}
	if (enhance->manager)
		dispdev = enhance->manager->device;
	if (dispdev) {
		dispdev->get_resolution(dispdev,
					&enhancep->config.info.size.width,
					&enhancep->config.info.size.height);
		if (dispdev->type == DISP_OUTPUT_TYPE_HDMI)
			enhancep->config.info.mode = (enhancep->config.info.mode
			    & 0xffff0000) | (1);	/* hdmi */
		else
			enhancep->config.info.mode = (enhancep->config.info.mode
			    & 0xffff0000) | (0);	/* lcd */
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.enable = 1;
	enhancep->config.flags |= ENH_ENABLE_DIRTY | ENH_SIZE_DIRTY;

	if ((enhancep->config.info.window.width == 0)
	    || (enhancep->config.info.window.height == 0)) {
		enhancep->config.info.window.width =
		    enhancep->config.info.size.width;
		enhancep->config.info.window.height =
		    enhancep->config.info.size.height;
	}

	enhancep->enabled = 1;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_disable(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.enable = 0;
	enhancep->config.flags |= ENH_ENABLE_DIRTY;

	enhancep->enabled = 0;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_demo_enable(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep = disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.demo_enable = 1;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_demo_disable(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep = disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.demo_enable = 0;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_set_mode(struct disp_enhance *enhance, u32 mode)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep = disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.mode = (enhancep->config.info.mode & 0xffff)  | (mode << 16);
	enhancep->config.flags |= ENH_MODE_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_get_mode(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep = disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.mode >> 16;
}

static s32 disp_enhance_set_bright(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.bright = value;
	enhancep->config.flags |= ENH_BRIGHT_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_get_bright(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.bright;
}

static s32 disp_enhance_set_saturation(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.saturation = value;
	enhancep->config.flags |= ENH_SAT_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_pq_update(struct disp_enhance *enhance, u32 *value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.bright = value[0];
	enhancep->config.info.contrast = value[1];
	enhancep->config.info.saturation = value[2];
	enhancep->config.info.hue = value[3];
	spin_unlock_irqrestore(&enhance_data_lock, flags);
	return 0;
}

static s32 disp_enhance_pq_apply(struct disp_enhance *enhance)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.flags |= ENH_ALL_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);
	disp_enhance_apply(enhance);
	return 0;
}

static s32 disp_enhance_get_saturation(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.saturation;
}

static s32 disp_enhance_set_contrast(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.contrast = value;
	enhancep->config.flags |= ENH_CONTRAST_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_get_contrast(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.contrast;
}

static s32 disp_enhance_set_edge(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.edge = value;
	enhancep->config.flags |= ENH_EDGE_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_get_edge(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.edge;
}

static s32 disp_enhance_set_detail(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.detail = value;
	enhancep->config.flags |= ENH_DETAIL_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_get_detail(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.detail;
}

static s32 disp_enhance_set_denoise(struct disp_enhance *enhance, u32 value)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhancep->config.info.denoise = value;
	enhancep->config.flags |= ENH_DNS_DIRTY;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	disp_enhance_apply(enhance);

	return 0;
}

static s32 disp_enhance_get_denoise(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((NULL == enhance) || (NULL == enhancep)) {
		DE_INFO("NULL hdl\n");
		return 0;
	}

	return enhancep->config.info.denoise;
}

static s32 disp_enhance_set_manager(struct disp_enhance *enhance,
				    struct disp_manager *mgr)
{
	unsigned long flags;

	if ((enhance == NULL) || (mgr == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	enhance->manager = mgr;
	mgr->enhance = enhance;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	return 0;
}

static s32 disp_enhance_unset_manager(struct disp_enhance *enhance)
{
	unsigned long flags;

	if (enhance == NULL) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	spin_lock_irqsave(&enhance_data_lock, flags);
	if (enhance->manager)
		enhance->manager->enhance = NULL;
	enhance->manager = NULL;
	spin_unlock_irqrestore(&enhance_data_lock, flags);

	return 0;
}

static s32 disp_enhance_shadow_protect(struct disp_enhance *enhance,
				       bool protect)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	struct disp_manager *mgr;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	mgr = enhance->manager;
	if (mgr && mgr->reg_protect)
		return mgr->reg_protect(mgr, protect);
	if (enhancep->shadow_protect)
		return enhancep->shadow_protect(enhance->disp, protect);

	return -1;
}

static s32 disp_enhance_dump(struct disp_enhance *enhance, char *buf)
{
	struct disp_enhance_config config;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	u32 count = 0;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	memcpy(&config, &enhancep->config, sizeof(struct disp_enhance_config));

	count += sprintf(buf + count, "enhance %d: %s, %s\n",
			 enhance->disp,
			 (config.info.enable == 1) ? "enable" : "disable",
			 (config.info.demo_enable == 1) ? "demo" : "normal");

	return count;
}

static s32 disp_enhance_init(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	return 0;
}

static s32 disp_enhance_exit(struct disp_enhance *enhance)
{
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}

	return 0;
}

/* for inner debug */
s32 disp_enhance_set_para(struct disp_enhance *enhance,
			  struct disp_enhance_para *para)
{
	unsigned long flags;
	struct disp_enhance_private_data *enhancep =
	    disp_enhance_get_priv(enhance);
	struct disp_device *dispdev = NULL;

	if ((enhance == NULL) || (enhancep == NULL)) {
		DE_INFO("NULL hdl\n");
		return -1;
	}
	if (enhance->manager)
		dispdev = enhance->manager->device;
	if (dispdev)
		dispdev->get_resolution(dispdev,
					&enhancep->config.info.size.width,
					&enhancep->config.info.size.height);

	spin_lock_irqsave(&enhance_data_lock, flags);
	memcpy(&enhancep->config.info.window, &para->window,
	       sizeof(struct disp_rect));
	enhancep->config.info.enable = para->enable;
	enhancep->config.flags |= ENH_ENABLE_DIRTY | ENH_SIZE_DIRTY;

	if ((enhancep->config.info.window.width == 0)
	    || (enhancep->config.info.window.height == 0)) {
		enhancep->config.info.window.width =
		    enhancep->config.info.size.width;
		enhancep->config.info.window.height =
		    enhancep->config.info.size.height;
	}

	enhancep->config.info.bright = para->bright;
	enhancep->config.info.contrast = para->contrast;
	enhancep->config.info.saturation = para->saturation;
	enhancep->config.info.hue = para->hue;
	enhancep->config.info.sharp = para->sharp;
	enhancep->config.info.auto_contrast = para->auto_contrast;

	enhancep->config.info.auto_color = para->auto_color;
	enhancep->config.info.fancycolor_red = para->fancycolor_red;
	enhancep->config.info.fancycolor_blue = para->fancycolor_blue;
	enhancep->config.info.fancycolor_green = para->fancycolor_green;

	enhancep->enabled = para->enable;
	spin_unlock_irqrestore(&enhance_data_lock, flags);
	DE_INFO
	    ("en=%d,para=%d,%d,%d,%d,sharp=%d,auto=%d,%d,fancycolor=%d,%d,%d\n",
	     enhancep->config.info.enable, enhancep->config.info.bright,
	     enhancep->config.info.contrast, enhancep->config.info.saturation,
	     enhancep->config.info.hue, enhancep->config.info.sharp,
	     enhancep->config.info.auto_color,
	     enhancep->config.info.auto_contrast,
	     enhancep->config.info.fancycolor_red,
	     enhancep->config.info.fancycolor_green,
	     enhancep->config.info.fancycolor_blue);

	disp_enhance_apply(enhance);
	return 0;
}

s32 disp_init_enhance(struct disp_bsp_init_para *para)
{
	u32 num_enhances;
	u32 disp;
	struct disp_enhance *enhance;
	struct disp_enhance_private_data *enhancep;

	DE_INFO("disp_init_enhance\n");

	spin_lock_init(&enhance_data_lock);
	num_enhances = bsp_disp_feat_get_num_screens();
	enhances =
	    kmalloc_array(num_enhances, sizeof(struct disp_enhance),
			  GFP_KERNEL | __GFP_ZERO);
	if (enhances == NULL) {
		DE_WARN("malloc memory fail\n");
		goto malloc_err;
	}
	enhance_private =
	    (struct disp_enhance_private_data *)
	    kmalloc(sizeof(struct disp_enhance_private_data)
		    * num_enhances, GFP_KERNEL | __GFP_ZERO);
	if (enhance_private == NULL) {
		DE_WARN("malloc memory fail\n");
		goto malloc_err;
	}

	for (disp = 0; disp < num_enhances; disp++) {
		if (bsp_disp_feat_is_support_enhance(disp) == 0)
			continue;

		enhance = &enhances[disp];
		enhancep = &enhance_private[disp];

		switch (disp) {
		case 0:
			enhance->name = "enhance0";
			enhance->disp = 0;
			break;
		case 1:
			enhance->name = "enhance1";
			enhance->disp = 1;
			break;
		case 2:
			enhance->name = "enhance2";
			enhance->disp = 2;

			break;
		}
		enhancep->shadow_protect = para->shadow_protect;

		enhance->enable = disp_enhance_enable;
		enhance->disable = disp_enhance_disable;
		enhance->is_enabled = disp_enhance_is_enabled;
		enhance->init = disp_enhance_init;
		enhance->exit = disp_enhance_exit;
		enhance->apply = disp_enhance_apply;
		enhance->update_regs = disp_enhance_update_regs;
		enhance->force_apply = disp_enhance_force_apply;
		enhance->sync = disp_enhance_sync;
		enhance->tasklet = disp_enhance_tasklet;
		enhance->set_manager = disp_enhance_set_manager;
		enhance->unset_manager = disp_enhance_unset_manager;
		enhance->set_para = disp_enhance_set_para;
		enhance->dump = disp_enhance_dump;
		enhance->demo_enable = disp_enhance_demo_enable;
		enhance->demo_disable = disp_enhance_demo_disable;
		enhance->set_mode = disp_enhance_set_mode;
		enhance->get_mode = disp_enhance_get_mode;
		enhance->set_bright = disp_enhance_set_bright;
		enhance->get_bright = disp_enhance_get_bright;
		enhance->set_saturation = disp_enhance_set_saturation;
		enhance->get_saturation = disp_enhance_get_saturation;
		enhance->set_contrast = disp_enhance_set_contrast;
		enhance->get_contrast = disp_enhance_get_contrast;
		enhance->set_edge = disp_enhance_set_edge;
		enhance->get_edge = disp_enhance_get_edge;
		enhance->set_detail = disp_enhance_set_detail;
		enhance->get_detail = disp_enhance_get_detail;
		enhance->set_denoise = disp_enhance_set_denoise;
		enhance->get_denoise = disp_enhance_get_denoise;

		enhance->pq_apply = disp_enhance_pq_apply;
		enhance->pq_update = disp_enhance_pq_update;
		enhance->init(enhance);
	}

	return 0;
malloc_err:
	kfree(enhance_private);
	kfree(enhances);
	enhance_private = NULL;
	enhances = NULL;

	return -1;
}

s32 disp_exit_enhance(void)
{
	u32 num_enhances;
	u32 disp;
	struct disp_enhance *enhance;

	if (!enhances)
		return 0;

	num_enhances = bsp_disp_feat_get_num_screens();
	for (disp = 0; disp < num_enhances; disp++) {
		if (bsp_disp_feat_is_support_enhance(disp) == 0)
			continue;

		enhance = &enhances[disp];
		enhance->exit(enhance);
	}

	kfree(enhance_private);
	kfree(enhances);

	return 0;
}

#endif
